#include "i2c.h"
#include "STC89C5xRC.H"

sbit SCL = P1 ^ 7; /* >! i2c_clk */
sbit SDA = P1 ^ 2; /* >! i2c_sda */

/**
 * @brief i2c_delay_4us
 *
 */
static void i2c_delay_4us(void)
{
    unsigned char data i;
    _nop_();
    _nop_();
    i = 8;
    while (--i)
        ;
}

/**
 * @brief i2c_start
 *
 */
static void i2c_start(void)
{
    SDA = 1;
    i2c_delay_4us();
    SCL = 1;
    i2c_delay_4us();
    SDA = 0;
    i2c_delay_4us();
    SCL = 0;
    i2c_delay_4us();
}

/**
 * @brief i2c_stop
 *
 */
static void i2c_stop(void)
{
    SCL = 0;
    i2c_delay_4us();
    SDA = 0;
    i2c_delay_4us();
    SCL = 1;
    i2c_delay_4us();
    SDA = 1;
    i2c_delay_4us();
}

/**
 * @brief i2c_receive_byte
 *
 * @return uint8_t
 */
static uint8_t i2c_receive_byte(void)
{
    uint8_t i, rbyte = 0;
    for (i = 8; i; i--)
    {
        SCL = 1;
        i2c_delay_4us();
        rbyte = rbyte << 1;
        rbyte = rbyte | (SDA);
        SCL = 0;
    }
    return rbyte;
}

/**
 * @brief i2c_send_byte
 *
 * @param dat
 */
static void i2c_send_byte(uint8_t dat)
{
    uint8_t i;
    for (i = 8; i; i--)
    {
        SDA = (bit)(dat & 0x80);
        i2c_delay_4us();
        SCL = 1;
        i2c_delay_4us();
        SCL = 0;
        i2c_delay_4us();
        dat = dat << 1;
    }
}

/**
 * @brief i2c_ack
 *
 * @param a
 */
static void i2c_ack(bool a)
{
    SDA = a;
    i2c_delay_4us();
    SCL = 1;
    i2c_delay_4us();
    SCL = 0;
    i2c_delay_4us();
    SDA = 1;
    i2c_delay_4us();
}

/**
 * @brief i2c_ack
 *
 * @return bool
 */
static bool test_ack(void)
{
    bool ack;
    SDA = 1;
    i2c_delay_4us();
    SCL = 1;
    i2c_delay_4us();
    ack = SDA;
    i2c_delay_4us();
    SCL = 0;
    return ack;
}

/**
 * @brief master_i2c_write_byte_to_device
 *
 * @param addr
 * @param *write_data
 * @param size
 * @return bool
 */
bool master_i2c_write_byte_to_device(uint8_t addr, uint8_t *write_data, uint8_t size)
{
    uint8_t i;

    i2c_start();

    i2c_send_byte((addr << 1) | 0x00); /* write addr and write_bit */
    if (test_ack())
        return (0);

    for (i = 0; i < size; i++)
    {
        i2c_send_byte(write_data[i]); /* write byte */
        if (test_ack())
            return (0);
    }

    i2c_stop();

    i2c_delay_4us();
}

/**
 * @brief master_i2c_write_byte_to_device
 *
 * @param addr
 * @param *read_data
 * @return bool
 */
bool master_i2c_read_byte_from_device(uint8_t addr, uint8_t *read_data)
{
    i2c_start();

    i2c_send_byte((addr << 1) | 0x01); /* write addr and read_bit */
    if (test_ack())
        return (0);

    *read_data = i2c_receive_byte(); /* read byte */
    i2c_ack(1);

    i2c_stop();
}
